﻿using System;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Linq;

using ScrewTurn.Wiki.PluginFramework;

using IdeaNMR.Shared;
using IdeaNMR.Web.Core;
using IdeaNMR.Shared.Utils;

namespace IdeaNMR.Web.FormatterPlugin
{
    public class IdeaNMRFormatter : IdeaNMRFormatterBase, IFormatterProviderV30
    {
        #region IFormatterProviderV30 Members

        public bool PerformPhase1
        {
            get { return false; }
        }

        public bool PerformPhase2
        {
            get { return false; }
        }

        public bool PerformPhase3
        {
            get { return true; }
        }

        public int ExecutionPriority
        {
            get { return 50; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="raw"></param>
        /// <param name="context"></param>
        /// <param name="phase"></param>
        /// <returns></returns>
        /// <remarks>
        /// <para>
        ///     The main action is to identify the correct url string for the <iframe> tag located in .htm file - embedded resource in the assembly.
        ///     The url should look like something:
        ///         <c>AllSetups.aspx</c>
        ///         <c>DesignExperiment.aspx?TargetID=[Guid]</c>
        ///         <c>DesignComponent.aspx?TargetID=[Guid]&CompType=[ComponentType]</c>
        ///         where [Guid] is <see cref="System.Guid"/> and [ComponentType] is <see cref="IdeaNMR.Core.ComponentType"/>.
        ///         The <c>TargetID</c> is optional.
        /// </para>
        /// <para>
        ///     The function parses <c>context.Page.Name</c> and <c>context.HttpContext.Request</c> parameters.
        ///     Three parameters need to be identified.
        ///         <c>context.Page.Name</c> must be of form 
        ///             <list type="bullet">
        ///                 <item>
        ///                     <term>SetupsAndComponents</term>
        ///                     <description>leads to <c>AllSetups.aspx</c></description>                
        ///                 </item>
        ///                 <item>
        ///                     <term>[word]_New or [word]_id_Guid</term>
        ///                     <description>Lead to <c>DesignExperiment.aspx?TargetID=[Guid]</c> or
        ///                         <c>DesignComponent.aspx?TargetID=[Guid]&CompType=[ComponentType]</c>
        ///                     </description>
        ///                 </item>
        ///             </list>        
        ///         <c>context.HttpContext.Request</c> may contain entry with key "TargetID"
        /// </para>
        /// <para>
        ///     <c>[word]_New or [word]_id_Guid</c> processed as following
        ///     <list type="bullet">
        ///     <item>
        ///         <term><c>targetType</c></term>
        ///         <description>The type of the component to be displayed. Is deduced from the [word] in <c>context.Page.Name</c>.</description>
        ///     </item>
        ///     <item>
        ///         <term><c>origID</c></term>
        ///         <description>The Guid of the first version deduced from Guid in <c>context.Page.Name</c>.</description>
        ///     </item>
        ///     <item>
        ///         <term><c>targetID</c></term>
        ///         <description>The Guid of the entry to be displayed. Is deduced from TargetID in <c>context.HttpContext.Request["TargetID"]</c>.</description>
        ///     </item>
        ///     </list>
        /// </para>
        /// <para>
        ///     If <c>targetID is missing</c> then
        ///     <para>
        ///         if <c>context.Page.Name</c> was [word]_New then, then it will just load an empty form, no TargetID at all.
        ///     </para>
        ///     <para>
        ///         if <c>context.Page.Name</c> was [word]_id_Guid, then <c>TargetID</c> will be assigned to the Guid, whose FirstVersion.Id is origID and VersionNumber is maximum.
        ///         That is there will be shown the last public version of the entry with given origID.
        ///     </para>
        /// </para>
        /// </remarks>
        public string Format(string raw, ContextInformation context, FormattingPhase phase)
        {
            if (context.Context != FormattingContext.PageContent) return raw;
            if (context.Page == null) return raw;


            string cPN = context.Page.FullName; // current Page Name

            // Here start the construction of final url
            UriBuilder uri = new UriBuilder();
            #region Specify Uri.Host, uri.Port & Get the path as array of segments
            // Get current host name
            uri.Host = context.HttpContext.Request.Url.Host;
            uri.Port = context.HttpContext.Request.Url.Port;

            // Get current path
            string[] segments = context.HttpContext.Request.Url.Segments;
            #endregion

            // The html template
            string frame = null;
            #region Get Template from embedded resource to string
            Assembly cAss = Assembly.GetExecutingAssembly();
            StreamReader textReader = new StreamReader(cAss.GetManifestResourceStream("IdeaNMR.Web.FormatterPlugin.Template.htm"));
            frame = textReader.ReadToEnd();
            #endregion

            // Reusable object to check what form the Page Name has
            Match urlParse;


            #region All Setups & Components
            urlParse = Regex.Match(cPN, @"^SetupsAndComponents$");
            if (urlParse.Success)
            {
                frame = ComposeFrame("IdeaNMR/AllSetups.aspx", segments, frame, uri);
                return frame + raw;
            }
            #endregion

            #region MySpectrometers
            urlParse = Regex.Match(cPN, @"^User_Spectrometers$");
            if (urlParse.Success)
            {
                frame = ComposeFrame("IdeaNMR/MySpectrometers.aspx", segments, frame, uri);
                return frame + raw;
            }
            #endregion

            #region MySamples
            urlParse = Regex.Match(cPN, @"^User_Samples$");
            if (urlParse.Success)
            {
                frame = ComposeFrame("IdeaNMR/MySamples.aspx", segments, frame, uri);
                return frame + raw;
            }
            #endregion

            #region MyExperiments
            urlParse = Regex.Match(cPN, @"^User_Experiments$");
            if (urlParse.Success)
            {
                frame = ComposeFrame("IdeaNMR/MyExperiments.aspx", segments, frame, uri);
                return frame + raw;
            }
            #endregion

            #region MyDrafts
            urlParse = Regex.Match(cPN, @"^User_Drafts$");
            if (urlParse.Success)
            {
                frame = ComposeFrame("IdeaNMR/MyDrafts.aspx", segments, frame, uri);
                return frame + raw;
            }
            #endregion

            #region
            urlParse = Regex.Match(cPN, @"^User_Profile$");
            if (urlParse.Success)
            {
                frame = ComposeFrame("IdeaNMR/AboutMe.aspx", segments, frame, uri);
                return frame + raw;
            }
            #endregion

            #region Add New Entity
            urlParse = Regex.Match(cPN, @"^(Sample|Spectrometer|PulseProgram|WaveShape|Cpd|Script)_New$");
            if (urlParse.Success)
            {
                Guid? targetID = null;
                string type = urlParse.Groups[1].Value;

                var cTargetID = context.HttpContext.Request["TargetID"];
                try
                {
                    if (cTargetID != null) targetID = new Guid(cTargetID);
                }
                catch (OverflowException)
                {
                    string errMsg = "IdeaNMRFormatter could not parse the TargetID parameter.<br />";
                    return errMsg + raw;
                }
                catch (FormatException)
                {
                    string errMsg = "IdeaNMRFormatter could not parse the TargetID parameter.<br />";
                    return errMsg + raw;
                }
                if (targetID.HasValue && targetID != Guid.Empty) uri.AddQueryParameter("TargetID", targetID.ToString());
                string loc = string.Format("IdeaNMR/Design{0}.aspx", type);
                frame = ComposeFrame(loc, segments, frame, uri);

                return frame + raw;
            }
            #endregion

            #region View Public Entity Page
            urlParse = Regex.Match(cPN, @"^(\w+)_id_([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})$");
            if (urlParse.Success)
            {
                Guid origID = Guid.Empty, targetID = Guid.Empty;
                EntityType? targetType = null;

                int cMatches = urlParse.Groups.Count;

                // [0] gives the whole string, [1] - word, [2] - Guid if present
                string cType = urlParse.Groups[1].Value;
                string cGuid = urlParse.Groups[2].Value;
                try
                {
                    targetType = (EntityType)Enum.Parse(typeof(EntityType), cType);
                    if (!string.IsNullOrEmpty(cGuid)) // first word and Guid => [word]_id_Guid form
                        origID = new Guid(cGuid);
                }
                catch (ArgumentException)
                {
                    string errMsg = string.Format("IdeaNMRFormatter could not deduce the component type from word '{0}'.<br />", cType);
                    return errMsg + raw;
                }

                var cTargetID = context.HttpContext.Request["TargetID"];
                try
                {
                    if (cTargetID != null) targetID = new Guid(cTargetID);
                }
                catch (OverflowException)
                {
                    string errMsg = "IdeaNMRFormatter could not parse the TargetID parameter.<br />";
                    return errMsg + raw;
                }
                catch (FormatException)
                {
                    string errMsg = "IdeaNMRFormatter could not parse the TargetID parameter.<br />";
                    return errMsg + raw;
                }
                if (origID != Guid.Empty)
                {
                    using (IdeaNMRdbContainer inmr = new IdeaNMRdbContainer())
                    {
                        // Find entry with given FirstVersionId == origId and VersionNumber is Highest
                        if (targetID == Guid.Empty)
                        {
                            targetID = (from BaseObject nmro in inmr.BaseObjects
                                        where nmro.FirstVersion.Id == origID &&
                                              !nmro.IsDraft && !nmro.IsPrivate
                                        group nmro by nmro.FirstVersion.Id into gr
                                        select gr.FirstOrDefault(x => x.Datetime == gr.Max(y => y.Datetime)).Id).FirstOrDefault();
                        }
                        // Check that given targetID corresponds to origID
                        // Entry with targetID must have FirstVersionId == origID
                        else
                        {
                            bool ent = (from BaseObject nmro in inmr.BaseObjects
                                        where nmro.Id == origID
                                        select nmro.DerivedVersions.Any(x => x.Id == targetID)).FirstOrDefault();
                            if (!ent)
                            {
                                string errMsg = string.Format("IdeaNMRFormatter found that Entry with ID {{{0}}} is not derived from entry with ID {{{1}}}.", targetID, origID);
                                return errMsg + raw;
                            }
                        }
                    }
                }

                // Deducing page name
                segments[segments.Length - 1] = string.Format("IdeaNMR/Design{0}.aspx", cType);
                //   uri.AddQueryParameter("CompType", targetType.ToString());
                uri.Path = string.Join(string.Empty, segments);
                if (targetID != Guid.Empty) uri.AddQueryParameter("TargetID", targetID.ToString());

                frame = frame.Replace("##URL##", uri.Uri.ToString());
                return frame + raw;
            }
            #endregion

            return raw;
        }


        public string PrepareTitle(string title, ContextInformation context)
        {
            return title;
        }

        #endregion

        #region Version Information
        // These will be initiated with the assembly Major, Minor and Build
        private static readonly string CurrentVersion;
        private static readonly string CurrentRevision;
        #endregion

        #region IProviderV30 Members
        private ComponentInformation _info = new ComponentInformation("NMR Experiment Formatter", "Vadim Slynko", CurrentVersion + CurrentRevision, string.Empty, string.Empty);
        public ComponentInformation Information
        {
            get { return _info; }
        }
        #endregion

        static IdeaNMRFormatter()
        {
            Version x = Assembly.GetExecutingAssembly().GetName().Version;
            CurrentVersion = string.Format("{0}.{1}", x.Major, x.Minor);
            CurrentRevision = string.Format(".{0}", x.Build);
        }

        private string ComposeFrame(string fileName, string[] segs, string frame, UriBuilder uri)
        {
            segs[segs.Length - 1] = fileName;
            uri.Path = string.Join(string.Empty, segs);
            return frame.Replace("##URL##", uri.Uri.ToString());
        }
    }
}
